{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "UEBilEjLj5wY"
   },
   "source": [
    "Deep Learning Models -- A collection of various deep learning architectures, models, and tips for TensorFlow and PyTorch in Jupyter Notebooks.\n",
    "- Author: Sebastian Raschka\n",
    "- GitHub Repository: https://github.com/rasbt/deeplearning-models"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 119
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 536,
     "status": "ok",
     "timestamp": 1524974472601,
     "user": {
      "displayName": "Sebastian Raschka",
      "photoUrl": "//lh6.googleusercontent.com/-cxK6yOSQ6uE/AAAAAAAAAAI/AAAAAAAAIfw/P9ar_CHsKOQ/s50-c-k-no/photo.jpg",
      "userId": "118404394130788869227"
     },
     "user_tz": 240
    },
    "id": "GOzuY8Yvj5wb",
    "outputId": "c19362ce-f87a-4cc2-84cc-8d7b4b9e6007"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sebastian Raschka \n",
      "\n",
      "CPython 3.6.8\n",
      "IPython 7.2.0\n",
      "\n",
      "torch 1.0.1.post2\n"
     ]
    }
   ],
   "source": [
    "%load_ext watermark\n",
    "%watermark -a 'Sebastian Raschka' -v -p torch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "rH4XmErYj5wm"
   },
   "source": [
    "# Model Zoo -- AlexNet CIFAR-10 Classifier"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Network Architecture"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "References\n",
    "    \n",
    "- [1] Krizhevsky, Alex, Ilya Sutskever, and Geoffrey E. Hinton. \"[Imagenet classification with deep convolutional neural networks.](https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf)\" In Advances in Neural Information Processing Systems, pp. 1097-1105. 2012.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "MkoGLH_Tj5wn"
   },
   "source": [
    "## Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "ORj09gnrj5wp"
   },
   "outputs": [],
   "source": [
    "import os\n",
    "import time\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "from torch.utils.data import DataLoader\n",
    "from torch.utils.data.sampler import SubsetRandomSampler\n",
    "\n",
    "from torchvision import datasets\n",
    "from torchvision import transforms\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "from PIL import Image\n",
    "\n",
    "\n",
    "if torch.cuda.is_available():\n",
    "    torch.backends.cudnn.deterministic = True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "I6hghKPxj5w0"
   },
   "source": [
    "## Model Settings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 85
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 23936,
     "status": "ok",
     "timestamp": 1524974497505,
     "user": {
      "displayName": "Sebastian Raschka",
      "photoUrl": "//lh6.googleusercontent.com/-cxK6yOSQ6uE/AAAAAAAAAAI/AAAAAAAAIfw/P9ar_CHsKOQ/s50-c-k-no/photo.jpg",
      "userId": "118404394130788869227"
     },
     "user_tz": 240
    },
    "id": "NnT0sZIwj5wu",
    "outputId": "55aed925-d17e-4c6a-8c71-0d9b3bde5637"
   },
   "outputs": [],
   "source": [
    "##########################\n",
    "### SETTINGS\n",
    "##########################\n",
    "\n",
    "# Hyperparameters\n",
    "RANDOM_SEED = 1\n",
    "LEARNING_RATE = 0.0001\n",
    "BATCH_SIZE = 256\n",
    "NUM_EPOCHS = 20\n",
    "\n",
    "# Architecture\n",
    "NUM_CLASSES = 10\n",
    "\n",
    "# Other\n",
    "DEVICE = \"cuda:0\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_train_valid_loader(data_dir,\n",
    "                           batch_size,\n",
    "                           train_transform,\n",
    "                           valid_transform,\n",
    "                           random_seed,\n",
    "                           valid_size=0.1,\n",
    "                           shuffle=True,\n",
    "                           num_workers=4):\n",
    "\n",
    "    train_dataset = datasets.CIFAR10(root=data_dir,\n",
    "                                     train=True,\n",
    "                                     download=True,\n",
    "                                     transform=train_transform)\n",
    "\n",
    "    valid_dataset = datasets.CIFAR10(root=data_dir,\n",
    "                                     train=True,\n",
    "                                     download=False,\n",
    "                                     transform=valid_transform)\n",
    "\n",
    "    num_train = len(train_dataset)\n",
    "    indices = np.arange(num_train)\n",
    "    split = np.int(np.floor(valid_size * num_train))\n",
    "\n",
    "    if shuffle:\n",
    "        np.random.seed(random_seed)\n",
    "        np.random.shuffle(indices)\n",
    "\n",
    "    train_idx, valid_idx = indices[split:], indices[:split]\n",
    "    train_sampler = SubsetRandomSampler(train_idx)\n",
    "    valid_sampler = SubsetRandomSampler(valid_idx)\n",
    "\n",
    "    train_loader = torch.utils.data.DataLoader(train_dataset,\n",
    "                                               batch_size=batch_size,\n",
    "                                               sampler=train_sampler,\n",
    "                                               num_workers=num_workers)\n",
    "\n",
    "    valid_loader = torch.utils.data.DataLoader(valid_dataset,\n",
    "                                               batch_size=batch_size,\n",
    "                                               sampler=valid_sampler,\n",
    "                                               num_workers=num_workers)\n",
    "\n",
    "    return (train_loader, valid_loader)\n",
    "\n",
    "\n",
    "def get_test_loader(data_dir,\n",
    "                    batch_size,\n",
    "                    test_transform,\n",
    "                    num_workers=4):\n",
    "\n",
    "    dataset = datasets.CIFAR10(root=data_dir,\n",
    "                               train=False,\n",
    "                               download=False,\n",
    "                               transform=test_transform)\n",
    "\n",
    "    data_loader = torch.utils.data.DataLoader(dataset,\n",
    "                                              batch_size=batch_size,\n",
    "                                              num_workers=num_workers)\n",
    "\n",
    "    return data_loader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Files already downloaded and verified\n",
      "Training Set:\n",
      "\n",
      "Image batch dimensions: torch.Size([256, 3, 64, 64])\n",
      "Image label dimensions: torch.Size([256])\n",
      "\n",
      "Validation Set:\n",
      "Image batch dimensions: torch.Size([256, 3, 64, 64])\n",
      "Image label dimensions: torch.Size([256])\n",
      "\n",
      "Testing Set:\n",
      "Image batch dimensions: torch.Size([256, 3, 64, 64])\n",
      "Image label dimensions: torch.Size([256])\n"
     ]
    }
   ],
   "source": [
    "##########################\n",
    "### CIFAR-10 Dataset\n",
    "##########################\n",
    "\n",
    "\n",
    "custom_transform = transforms.Compose([transforms.Resize((64, 64)),\n",
    "                                       transforms.ToTensor()])\n",
    "\n",
    "train_loader, valid_loader = get_train_valid_loader(\n",
    "    data_dir='data',\n",
    "    batch_size=BATCH_SIZE,\n",
    "    train_transform=custom_transform,\n",
    "    valid_transform=custom_transform,\n",
    "    random_seed=RANDOM_SEED,\n",
    "    valid_size=0.1,\n",
    "    shuffle=True,\n",
    "    num_workers=4\n",
    ")\n",
    "\n",
    "test_loader = get_test_loader(\n",
    "    data_dir='data',\n",
    "    batch_size=BATCH_SIZE,\n",
    "    test_transform=custom_transform,\n",
    "    num_workers=4\n",
    ")\n",
    "\n",
    "\n",
    "# Checking the dataset\n",
    "print('Training Set:\\n')\n",
    "for images, labels in train_loader:  \n",
    "    print('Image batch dimensions:', images.size())\n",
    "    print('Image label dimensions:', labels.size())\n",
    "    break\n",
    "    \n",
    "# Checking the dataset\n",
    "print('\\nValidation Set:')\n",
    "for images, labels in valid_loader:  \n",
    "    print('Image batch dimensions:', images.size())\n",
    "    print('Image label dimensions:', labels.size())\n",
    "    break\n",
    "\n",
    "# Checking the dataset\n",
    "print('\\nTesting Set:')\n",
    "for images, labels in train_loader:  \n",
    "    print('Image batch dimensions:', images.size())\n",
    "    print('Image label dimensions:', labels.size())\n",
    "    break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "##########################\n",
    "### MODEL\n",
    "##########################\n",
    "\n",
    "class AlexNet(nn.Module):\n",
    "\n",
    "    def __init__(self, num_classes):\n",
    "        super(AlexNet, self).__init__()\n",
    "        self.features = nn.Sequential(\n",
    "            nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.MaxPool2d(kernel_size=3, stride=2),\n",
    "            nn.Conv2d(64, 192, kernel_size=5, padding=2),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.MaxPool2d(kernel_size=3, stride=2),\n",
    "            nn.Conv2d(192, 384, kernel_size=3, padding=1),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.Conv2d(384, 256, kernel_size=3, padding=1),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.Conv2d(256, 256, kernel_size=3, padding=1),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.MaxPool2d(kernel_size=3, stride=2),\n",
    "        )\n",
    "        self.avgpool = nn.AdaptiveAvgPool2d((6, 6))\n",
    "        self.classifier = nn.Sequential(\n",
    "            nn.Dropout(0.5),\n",
    "            nn.Linear(256 * 6 * 6, 4096),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.Dropout(0.5),\n",
    "            nn.Linear(4096, 4096),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.Linear(4096, num_classes)\n",
    "        )\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.features(x)\n",
    "        x = self.avgpool(x)\n",
    "        x = x.view(x.size(0), 256 * 6 * 6)\n",
    "        logits = self.classifier(x)\n",
    "        probas = F.softmax(logits, dim=1)\n",
    "        return logits, probas\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "_lza9t_uj5w1"
   },
   "outputs": [],
   "source": [
    "torch.manual_seed(RANDOM_SEED)\n",
    "\n",
    "model = AlexNet(NUM_CLASSES)\n",
    "model.to(DEVICE)\n",
    "\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "RAodboScj5w6"
   },
   "source": [
    "## Training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 1547
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 2384585,
     "status": "ok",
     "timestamp": 1524976888520,
     "user": {
      "displayName": "Sebastian Raschka",
      "photoUrl": "//lh6.googleusercontent.com/-cxK6yOSQ6uE/AAAAAAAAAAI/AAAAAAAAIfw/P9ar_CHsKOQ/s50-c-k-no/photo.jpg",
      "userId": "118404394130788869227"
     },
     "user_tz": 240
    },
    "id": "Dzh3ROmRj5w7",
    "outputId": "5f8fd8c9-b076-403a-b0b7-fd2d498b48d7",
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 001/020 | Batch 0000/0176 | Cost: 2.3027\n",
      "Epoch: 001/020 | Batch 0150/0176 | Cost: 1.5725\n",
      "Epoch: 001/020 | Train: 36.202%  | Valid: 35.380%\n",
      "Time elapsed: 0.20 min\n",
      "Epoch: 002/020 | Batch 0000/0176 | Cost: 1.6128\n",
      "Epoch: 002/020 | Batch 0150/0176 | Cost: 1.4558\n",
      "Epoch: 002/020 | Train: 46.247%  | Valid: 46.040%\n",
      "Time elapsed: 0.39 min\n",
      "Epoch: 003/020 | Batch 0000/0176 | Cost: 1.4093\n",
      "Epoch: 003/020 | Batch 0150/0176 | Cost: 1.1841\n",
      "Epoch: 003/020 | Train: 53.956%  | Valid: 52.540%\n",
      "Time elapsed: 0.58 min\n",
      "Epoch: 004/020 | Batch 0000/0176 | Cost: 1.2018\n",
      "Epoch: 004/020 | Batch 0150/0176 | Cost: 1.1820\n",
      "Epoch: 004/020 | Train: 57.549%  | Valid: 55.160%\n",
      "Time elapsed: 0.78 min\n",
      "Epoch: 005/020 | Batch 0000/0176 | Cost: 1.0970\n",
      "Epoch: 005/020 | Batch 0150/0176 | Cost: 1.1353\n",
      "Epoch: 005/020 | Train: 62.196%  | Valid: 58.820%\n",
      "Time elapsed: 0.98 min\n",
      "Epoch: 006/020 | Batch 0000/0176 | Cost: 0.9871\n",
      "Epoch: 006/020 | Batch 0150/0176 | Cost: 1.0954\n",
      "Epoch: 006/020 | Train: 62.413%  | Valid: 58.780%\n",
      "Time elapsed: 1.18 min\n",
      "Epoch: 007/020 | Batch 0000/0176 | Cost: 1.0950\n",
      "Epoch: 007/020 | Batch 0150/0176 | Cost: 0.9522\n",
      "Epoch: 007/020 | Train: 68.949%  | Valid: 62.860%\n",
      "Time elapsed: 1.37 min\n",
      "Epoch: 008/020 | Batch 0000/0176 | Cost: 0.8985\n",
      "Epoch: 008/020 | Batch 0150/0176 | Cost: 0.8718\n",
      "Epoch: 008/020 | Train: 67.489%  | Valid: 60.260%\n",
      "Time elapsed: 1.57 min\n",
      "Epoch: 009/020 | Batch 0000/0176 | Cost: 0.9852\n",
      "Epoch: 009/020 | Batch 0150/0176 | Cost: 0.8458\n",
      "Epoch: 009/020 | Train: 74.444%  | Valid: 64.880%\n",
      "Time elapsed: 1.77 min\n",
      "Epoch: 010/020 | Batch 0000/0176 | Cost: 0.7606\n",
      "Epoch: 010/020 | Batch 0150/0176 | Cost: 0.7054\n",
      "Epoch: 010/020 | Train: 77.271%  | Valid: 66.240%\n",
      "Time elapsed: 1.96 min\n",
      "Epoch: 011/020 | Batch 0000/0176 | Cost: 0.6426\n",
      "Epoch: 011/020 | Batch 0150/0176 | Cost: 0.7225\n",
      "Epoch: 011/020 | Train: 78.740%  | Valid: 65.580%\n",
      "Time elapsed: 2.16 min\n",
      "Epoch: 012/020 | Batch 0000/0176 | Cost: 0.6353\n",
      "Epoch: 012/020 | Batch 0150/0176 | Cost: 0.7038\n",
      "Epoch: 012/020 | Train: 81.960%  | Valid: 66.500%\n",
      "Time elapsed: 2.36 min\n",
      "Epoch: 013/020 | Batch 0000/0176 | Cost: 0.5080\n",
      "Epoch: 013/020 | Batch 0150/0176 | Cost: 0.5161\n",
      "Epoch: 013/020 | Train: 85.880%  | Valid: 68.320%\n",
      "Time elapsed: 2.56 min\n",
      "Epoch: 014/020 | Batch 0000/0176 | Cost: 0.4111\n",
      "Epoch: 014/020 | Batch 0150/0176 | Cost: 0.4817\n",
      "Epoch: 014/020 | Train: 86.342%  | Valid: 68.480%\n",
      "Time elapsed: 2.76 min\n",
      "Epoch: 015/020 | Batch 0000/0176 | Cost: 0.3986\n",
      "Epoch: 015/020 | Batch 0150/0176 | Cost: 0.5016\n",
      "Epoch: 015/020 | Train: 86.967%  | Valid: 67.040%\n",
      "Time elapsed: 2.96 min\n",
      "Epoch: 016/020 | Batch 0000/0176 | Cost: 0.3665\n",
      "Epoch: 016/020 | Batch 0150/0176 | Cost: 0.4297\n",
      "Epoch: 016/020 | Train: 89.004%  | Valid: 66.100%\n",
      "Time elapsed: 3.16 min\n",
      "Epoch: 017/020 | Batch 0000/0176 | Cost: 0.3275\n",
      "Epoch: 017/020 | Batch 0150/0176 | Cost: 0.2950\n",
      "Epoch: 017/020 | Train: 92.491%  | Valid: 68.980%\n",
      "Time elapsed: 3.35 min\n",
      "Epoch: 018/020 | Batch 0000/0176 | Cost: 0.2108\n",
      "Epoch: 018/020 | Batch 0150/0176 | Cost: 0.2200\n",
      "Epoch: 018/020 | Train: 93.887%  | Valid: 68.340%\n",
      "Time elapsed: 3.55 min\n",
      "Epoch: 019/020 | Batch 0000/0176 | Cost: 0.1458\n",
      "Epoch: 019/020 | Batch 0150/0176 | Cost: 0.1814\n",
      "Epoch: 019/020 | Train: 93.922%  | Valid: 67.540%\n",
      "Time elapsed: 3.75 min\n",
      "Epoch: 020/020 | Batch 0000/0176 | Cost: 0.2147\n",
      "Epoch: 020/020 | Batch 0150/0176 | Cost: 0.1742\n",
      "Epoch: 020/020 | Train: 91.998%  | Valid: 66.900%\n",
      "Time elapsed: 3.95 min\n",
      "Total Training Time: 3.95 min\n",
      "Test accuracy: 66.63%\n",
      "Total Time: 3.96 min\n"
     ]
    }
   ],
   "source": [
    "def compute_accuracy(model, data_loader, device):\n",
    "    correct_pred, num_examples = 0, 0\n",
    "    model.eval()\n",
    "    for i, (features, targets) in enumerate(data_loader):\n",
    "            \n",
    "        features = features.to(device)\n",
    "        targets = targets.to(device)\n",
    "\n",
    "        logits, probas = model(features)\n",
    "        _, predicted_labels = torch.max(probas, 1)\n",
    "        num_examples += targets.size(0)\n",
    "        assert predicted_labels.size() == targets.size()\n",
    "        correct_pred += (predicted_labels == targets).sum()\n",
    "    return correct_pred.float()/num_examples * 100\n",
    "    \n",
    "\n",
    "start_time = time.time()\n",
    "for epoch in range(NUM_EPOCHS):\n",
    "    \n",
    "    model.train()\n",
    "    for batch_idx, (features, targets) in enumerate(train_loader):\n",
    "        \n",
    "        features = features.to(DEVICE)\n",
    "        targets = targets.to(DEVICE)\n",
    "            \n",
    "        ### FORWARD AND BACK PROP\n",
    "        logits, probas = model(features)\n",
    "        cost = F.cross_entropy(logits, targets)\n",
    "        optimizer.zero_grad()\n",
    "        \n",
    "        cost.backward()\n",
    "        \n",
    "        ### UPDATE MODEL PARAMETERS\n",
    "        optimizer.step()\n",
    "        \n",
    "        ### LOGGING\n",
    "        if not batch_idx % 150:\n",
    "            print ('Epoch: %03d/%03d | Batch %04d/%04d | Cost: %.4f' \n",
    "                   %(epoch+1, NUM_EPOCHS, batch_idx, \n",
    "                     len(train_loader), cost))\n",
    "\n",
    "        \n",
    "\n",
    "    model.eval()\n",
    "    with torch.set_grad_enabled(False): # save memory during inference\n",
    "        print('Epoch: %03d/%03d | Train: %.3f%%  | Valid: %.3f%%' % (\n",
    "              epoch+1, NUM_EPOCHS, \n",
    "              compute_accuracy(model, train_loader, device=DEVICE),\n",
    "              compute_accuracy(model, valid_loader, device=DEVICE)))\n",
    "        \n",
    "    print('Time elapsed: %.2f min' % ((time.time() - start_time)/60))\n",
    "    \n",
    "print('Total Training Time: %.2f min' % ((time.time() - start_time)/60))\n",
    "\n",
    "\n",
    "with torch.set_grad_enabled(False): # save memory during inference\n",
    "    print('Test accuracy: %.2f%%' % (compute_accuracy(model, test_loader, device=DEVICE)))\n",
    "    \n",
    "print('Total Time: %.2f min' % ((time.time() - start_time)/60))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "numpy       1.15.4\n",
      "pandas      0.23.4\n",
      "torch       1.0.1.post2\n",
      "PIL.Image   5.3.0\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%watermark -iv"
   ]
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "collapsed_sections": [],
   "default_view": {},
   "name": "convnet-vgg16.ipynb",
   "provenance": [],
   "version": "0.3.2",
   "views": {}
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  },
  "toc": {
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": true,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "371px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
